home *** CD-ROM | disk | FTP | other *** search
/ Chip: 2005 Utilities / CHIP Utilities 2005.7z / CHIP Utilities 2005.iso / tools / convert.c next >
C/C++ Source or Header  |  2003-12-19  |  19KB  |  575 lines

  1. /* CD Shell Image conversion tool.
  2.  * Copyright (C) 2002-2004 Michael K Ter Louw
  3.  *
  4.  * Version 1.1
  5.  *  - Can convert both directions: bmp->csi and csi->bmp.
  6.  *
  7.  * Version 1.0
  8.  *  - First release.  Converts BMP files to CSI format.
  9.  */
  10.  
  11. #include <ctype.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14.  
  15. /* Image conversion modes. */
  16. #define BMP_2_CSI 1
  17. #define CSI_2_BMP 2
  18.  
  19. /* Bitmap image format options. */
  20. #define BMP_WINDOWS           0
  21. #define BMP_OS2               1
  22.  
  23. /* Bitmap file headers. */
  24. char bmp_file_header[] =
  25. {  'B', 'M',                  /* BMP signature */
  26.    0x36, 0x10, 0x0E, 0x00,    /* File size */
  27.    0x00, 0x00, 0x00, 0x00,    /* Reserved space */
  28.    0x36, 0x00, 0x00, 0x00     /* Image offset (for Windows format) */
  29. };
  30.  
  31. char bmp_info_header[] =
  32. {  0x28, 0x00, 0x00, 0x00,    /* Info header size (for Windows format) */
  33.    0x80, 0x02, 0x00, 0x00,    /* Image width (640 pixels) */
  34.    0xE0, 0x01, 0x00, 0x00,    /* Image height (480 pixels) */
  35.    0x01, 0x00,                /* Number of planes */
  36.    0x18, 0x00                 /* Color depth (24bpp) */
  37. };
  38.  
  39. char bmp_extra_header[] =
  40. {  0x00, 0x00, 0x00, 0x00,    /* Compression type (none) */
  41.    0x00, 0x10, 0x0E, 0x00,    /* Image data size (921600 bytes) */
  42.    0x13, 0x0B, 0x00, 0x00,    /* Horizontal pixels per meter */
  43.    0x13, 0x0B, 0x00, 0x00,    /* Vertical pixels per meter */
  44.    0x00, 0x00, 0x00, 0x00,    /* Number of colors used (i.e., palette size) */
  45.    0x00, 0x00, 0x00, 0x00     /* Number of important colors */
  46. };
  47.  
  48. /* CSI Encoding flags. */
  49. #define FL_BANK_TRIGGER_32    0x8000
  50. #define FL_BANK_TRIGGER_24    0x4000
  51. #define FL_SPLIT_PIXEL        0x2000
  52. #define FL_ABSOLUTE_BLOCK     0x2000
  53. #define FL_64KB_FILE_BOUNDARY 0x2000
  54.  
  55. /* CD Shell Image header. */
  56. const char * csi_header[] =
  57. {  "-CSI",
  58.    "CD Shell Image, v1.0",
  59.    "http://www.cdshell.org"
  60. };
  61.  
  62. /* Pixel boundaries that trigger a bank switch in 24bpp mode. */
  63. long bank_trigger_24[] =
  64. {  21845, 43690, 65536,
  65.    87381, 109226, 131072,
  66.    152917, 174762, 196608,
  67.    218453, 240298, 262144,
  68.    283989, 305834, 307200, /* Last value represents end of image. */
  69.    -1
  70. };
  71.  
  72. /* Pixel boundaries that trigger a bank switch in 32bpp mode. */
  73. long bank_trigger_32[] =
  74. {  16384, 32768, 49152, 65536,
  75.    81920, 98304, 114688, 131072,
  76.    147456, 163840, 180224, 196608,
  77.    212992, 229376, 245760, 262144,
  78.    278528, 294912,
  79.    -1
  80. };
  81.  
  82. /* Image data buffer.  Intermediate storage between decoding and encoding operations. */
  83. unsigned char image_data[640 * 480 * 3];
  84.  
  85. const char error_bmp_general[] = "Error reading input file, or unexpected end of file.\n";
  86. const char error_file_write[] = "Error writing to output file.\n";
  87.  
  88. /* Function prototypes. */
  89. int process_command_line(int , char **, int *, char **, char **);
  90. int decode_bmp(char *);
  91. int encode_bmp(char *, int);
  92. int decode_csi(char *);
  93. int encode_csi(char *);
  94. int simple_strnicmp(const char *, const char *, int);
  95.  
  96.  
  97. /* Program entry procedure. */
  98. int main(int argc, char **argv)
  99. {  int conversion = 0;
  100.    char *infile = NULL;
  101.    char *outfile = NULL;
  102.  
  103.    printf("\nCD Shell Image conversion utility, Version 1.1\n"
  104.       "Copyright (C) 2002-2004 Michael K Ter Louw\n"
  105.       "Visit http://www.cdshell.org for terms of use.\n\n");
  106.  
  107.    if (process_command_line(argc, argv, &conversion, &infile, &outfile))
  108.    {  printf("\nSyntax: convert <-bmp2csi | -csi2bmp> <-o outputfile> <inputfile>\n");
  109.       return 1;
  110.    }
  111.  
  112.    switch (conversion)
  113.    {  case BMP_2_CSI:
  114.          if (decode_bmp(infile)) return 1;
  115.          if (encode_csi(outfile)) return 1;
  116.          break;
  117.       case CSI_2_BMP:
  118.          if (decode_csi(infile)) return 1;
  119.          if (encode_bmp(outfile, BMP_WINDOWS)) return 1;
  120.    }
  121.    printf("Image encoded successfully.\n");
  122.    return 0;
  123. }
  124.  
  125.  
  126.  
  127.  
  128. /* Process command line arguments. */
  129. int process_command_line(int argc, char *argv[], int *conversion, char **infile, char **outfile)
  130. {  int h;
  131.  
  132.    for (h = 1; h < argc; h++)
  133.    {  /* Check for input file. */
  134.       if (*(argv[h]) != '-')
  135.       {  if (*infile != NULL)
  136.          {  printf("Error: Input file already specified.\n");
  137.             return 1;
  138.          }
  139.          *infile = argv[h]; continue;
  140.       }
  141.  
  142.       /* Handle command switches. */
  143.       if (!simple_strnicmp(argv[h], "-bmp2csi", 9))
  144.       {  if (*conversion)
  145.          {  printf("Conversion mode already specified.\n");
  146.             return 1;
  147.          }
  148.          *conversion = BMP_2_CSI;
  149.          continue;
  150.       }
  151.  
  152.       if (!simple_strnicmp(argv[h], "-csi2bmp", 9))
  153.       {  if (*conversion)
  154.          {  printf("Conversion mode already specified.\n");
  155.             return 1;
  156.          }
  157.          *conversion = CSI_2_BMP;
  158.          continue;
  159.       }
  160.  
  161.       if (!simple_strnicmp(argv[h], "-o", 3))
  162.       {  if (*outfile != NULL)
  163.          {  printf("Error: Output file already specified.\n");
  164.             return 1;
  165.          }
  166.          if (h == argc - 1)
  167.          {  printf("Error: Output file expected.\n");
  168.             return 1;
  169.          }
  170.          h++; *outfile = argv[h]; continue;
  171.       }
  172.  
  173.       if (!simple_strnicmp(argv[h], "-v", 3))
  174.       {
  175.       }
  176.  
  177.       printf("Error: Unrecognized option: \"%s\"\n", argv[h]);
  178.       return 1;
  179.    }
  180.  
  181.    /* Check to see if parameters were not defined. */
  182.    if (!(*conversion))
  183.    {  printf("Error: Conversion mode not specified.\n");
  184.       return 1;
  185.    }
  186.    if (!(*infile))
  187.    {  printf("Error: Input file not specified.\n");
  188.       return 1;
  189.    }
  190.    if (!(*outfile))
  191.    {  printf("Error: Output file not specified.\n");
  192.       return 1;
  193.    }
  194.    return 0;
  195. }
  196.  
  197.  
  198.  
  199.  
  200. /* Decode a bitmap file. */
  201. int decode_bmp(char * filename)
  202. {  long buffer_index, h;
  203.    long image_offset = 14;
  204.    unsigned short p[2];
  205.    unsigned long q[2];
  206.    unsigned char c, d, e[2];
  207.    FILE * input_file;
  208.  
  209.    /* Read BMP file header.   */
  210.    if (!(input_file = fopen(filename, "rb")))
  211.    {  printf("Error opening input file: \"%s\"\n", filename);
  212.       return 1;
  213.    }
  214.    /* Verify BMP signature.   */
  215.    if ((fread(e, 2, 1, input_file) < 1) || e[0] != bmp_file_header[0] ||
  216.       e[1] != bmp_file_header[1])
  217.    {  printf(error_bmp_general); fclose(input_file); return 1;
  218.    }
  219.    /* Determine image offset (different depending on Windows or OS/2 format. */
  220.    if (fseek(input_file, 14, SEEK_SET))
  221.    {  printf(error_bmp_general); fclose(input_file); return 1;
  222.    }
  223.    if (fread(q, 4, 1, input_file) < 1)
  224.    {  printf(error_bmp_general); fclose(input_file); return 1;
  225.    }
  226.    image_offset += q[0];
  227.    /* Verify image width and height. */
  228.    if (fread(q, 4, 2, input_file) < 2)
  229.    {  printf(error_bmp_general); fclose(input_file); return 1;
  230.    }
  231.    if ((q[0] != 640) || (q[1] != 480))
  232.    {  printf("Error: BMP file is not 640 by 480.\n"); fclose(input_file); return 1;
  233.    }
  234.    /* Verify color depth. */
  235.    if (fread(p, 2, 2, input_file) < 2)
  236.    {  printf(error_bmp_general); fclose(input_file); return 1;
  237.    }
  238.    if (p[1] != 24)
  239.    {  printf("Error: BMP file is not 24 bits per pixel.\n"); fclose(input_file); return 1;
  240.    }
  241.    /* Read the image data. */
  242.    if (fseek(input_file, image_offset, SEEK_SET))
  243.    {  printf(error_bmp_general); fclose(input_file); return 1;
  244.    }
  245.    printf("Bitmap image verified 640x480x24.\n");
  246.    buffer_index = 640 * 479 * 3;
  247.    for (h = 0; h < 480; buffer_index -= 640 * 3, h++)
  248.    {  if ((fread(image_data + buffer_index, 640 * 3, 1, input_file)) != 1)
  249.       {  printf(error_bmp_general); fclose(input_file); return 1;
  250.       }
  251.    }
  252.    fclose(input_file);
  253.    return 0;
  254. }
  255.  
  256.  
  257.  
  258.  
  259. /* Create a BMP image file. */
  260. int encode_bmp(char * filename, int format)
  261. {  int buffer_index;
  262.    int h, i;
  263.    FILE * output_file;
  264.  
  265.    /* Open the output file. */
  266.    if (!(output_file = fopen(filename, "wb")))
  267.    {  printf("Couldn't open output file \"%s\".\n", filename);
  268.       return 1;
  269.    }
  270.  
  271.    printf("Encoding Bitmap (BMP) Image...\n");
  272.  
  273.    /* Generate header. */
  274.    if (format)
  275.    {  /* Set header values to OS/2 format. */
  276.       bmp_file_header[10] = 26;
  277.       bmp_info_header[0] = 12;
  278.    }
  279.    fwrite(bmp_file_header, 14, 1, output_file);
  280.    fwrite(bmp_info_header, 16, 1, output_file);
  281.    fwrite(bmp_extra_header, 24, 1, output_file);
  282.  
  283.    for (h = 0; h < 480; h++)
  284.    {  buffer_index = 640 * 3 * (479 - h);
  285.       for (i = 0; i < 640 * 3; buffer_index++, i++)
  286.          fputc(image_data[buffer_index], output_file);
  287.    }
  288.    fclose(output_file);
  289.    return 0;
  290.  
  291. }
  292.  
  293.  
  294.  
  295. /* Decode a CD Shell Image (Version 1) file. */
  296. int decode_csi(char * filename)
  297. {  long buffer_index, block_code, file_pos = 0x40;
  298.    unsigned char c[6];
  299.    FILE * input_file;
  300.  
  301.    /* Read CSI file header.   */
  302.    if (!(input_file = fopen(filename, "rb")))
  303.    {  printf("Error opening input file: \"%s\"\n", filename);
  304.       return 1;
  305.    }
  306.    /* Verify CSI signature.   */
  307.    if ((fread(c, 4, 1, input_file) < 1) || (c[0] != '-') ||
  308.       (c[1] != 'C')  || (c[2] != 'S')  || (c[3] != 'I'))
  309.    {  printf(error_bmp_general); fclose(input_file); return 1;
  310.    }
  311.    /* Verify CSI version.  */
  312.    if ((fread(c, 2, 1, input_file) < 1) || (c[0] != 1) || (c[1] != 0))
  313.    {  printf(error_bmp_general); fclose(input_file); return 1;
  314.    }
  315.    printf("CD Shell Image format verified 640x480x24.\n");
  316.    /* Read the rest of the header.  */
  317.    if ((fread(image_data, 0x40 - 6, 1, input_file) < 1))
  318.    {  printf(error_bmp_general); fclose(input_file); return 1;
  319.    }
  320.  
  321.    /* Decode encoded blocks. */
  322.    for (buffer_index = 0; 1; )
  323.    {  /* Read the block code. */
  324.       if ((fread(c, 2, 1, input_file) < 1))
  325.       {  printf(error_bmp_general); fclose(input_file); return 1;
  326.       }
  327.       block_code = (long) (c[1] << 8) + c[0];
  328.       file_pos += 2;
  329.  
  330.       /* Check for end of file reached. */
  331.       if (!block_code)
  332.       {  if (buffer_index != 640 * 480 * 3)
  333.          {  printf(error_bmp_general); fclose(input_file); return 1;
  334.          }
  335.          return 0;
  336.       }
  337.  
  338.       /* Handle file-block position markers. */
  339.       if (block_code == FL_64KB_FILE_BOUNDARY)
  340.       {  if ((file_pos % 65536) && (fread(c, 65536 - (file_pos % 65536), 1, input_file) < 1))
  341.          {  printf(error_bmp_general); fclose(input_file); return 1;
  342.          }
  343.          file_pos = (file_pos + 65535) & ~65535;
  344.          continue;
  345.       }
  346.  
  347.       /* Handle 24bpp bank triggers. */
  348.       if (block_code & FL_BANK_TRIGGER_24)
  349.       {  if (block_code & FL_SPLIT_PIXEL)
  350.          {  if ((fread(image_data + buffer_index, 3, 1, input_file) < 1))
  351.             {  printf(error_bmp_general); fclose(input_file); return 1;
  352.             }
  353.             buffer_index += 3; file_pos += 3;
  354.          }
  355.          continue;
  356.       }
  357.  
  358.       /* Ignore 32bpp bank triggers. */
  359.       else if (block_code & FL_BANK_TRIGGER_32) continue;
  360.  
  361.       /* Handle absolute blocks. */
  362.       if (block_code & FL_ABSOLUTE_BLOCK)
  363.       {  block_code ^= FL_ABSOLUTE_BLOCK;
  364.          file_pos += block_code * 3;
  365.          for (; block_code; block_code--)
  366.          {  if ((fread(image_data + buffer_index, 3, 1, input_file) < 1))
  367.             {  printf(error_bmp_general); fclose(input_file); return 1;
  368.             }
  369.             buffer_index += 3;
  370.          }
  371.          continue;
  372.       }
  373.  
  374.       /* Handle repeat blocks. */
  375.       else
  376.       {  if ((fread(c, 3, 1, input_file) < 1))
  377.          {  printf(error_bmp_general); fclose(input_file); return 1;
  378.          }
  379.          for (; block_code; block_code--)
  380.          {  image_data[buffer_index + 0] = c[0];
  381.             image_data[buffer_index + 1] = c[1];
  382.             image_data[buffer_index + 2] = c[2];
  383.             buffer_index += 3;
  384.          }
  385.          file_pos += 3;
  386.          continue;
  387.       }
  388.    }
  389.    fclose(input_file);
  390.    return 0;
  391. }
  392.  
  393.  
  394.  
  395.  
  396. /* Create a CD Shell Image file (Version 1 format).   */
  397. int encode_csi(char * filename)
  398. {  int h, i;
  399.    int file_block_pos;
  400.    unsigned char r, g, b;
  401.    FILE * output_file;
  402.  
  403.    long current_pixel = 0;
  404.    long file_pos;
  405.    long block_code;
  406.    long repeat_count;
  407.    long absolute_count;
  408.  
  409.  
  410.    if (!(output_file = fopen(filename, "wb")))
  411.    {  printf("Couldn't open output file \"%s\".\n", filename);
  412.       return 1;
  413.    }
  414.  
  415.    printf("Encoding CD Shell Image (version 1)...\n");
  416.  
  417.    /* Generate header. */
  418.    fwrite(csi_header[0], 4, 1, output_file);
  419.    block_code = 1;      /* block_code = CD Shell Image version. */
  420.    fwrite(&block_code, 2, 1, output_file);
  421.    fwrite(csi_header[1], strlen(csi_header[1]) + 1, 1, output_file);
  422.    fwrite(csi_header[2], strlen(csi_header[2]) + 1, 1, output_file);
  423.    h = (int) (0x40 - (strlen(csi_header[1]) + strlen(csi_header[2]) + 8));
  424.    for (; h > 0; h--) fputc('\0', output_file);
  425.    file_pos = 0x40;
  426.  
  427.    /* Generate encoded blocks. */
  428.    for (block_code = 0; 1; block_code = 0)
  429.    {  /* Terminate if end of image has been reached. */
  430.       if (current_pixel == 640 * 480) break;
  431.  
  432.       /* Check for 64kb file boundary. */
  433.       file_block_pos = file_pos % 65536;
  434.       if (file_block_pos > (65536 - 7))
  435.       {  block_code = FL_64KB_FILE_BOUNDARY;
  436.          fwrite(&block_code, 2, 1, output_file); file_block_pos += 2;
  437.          for (; file_block_pos < 65536; file_block_pos++)
  438.             fputc('\0', output_file);
  439.          file_pos = (file_pos + 65535) & ~65535;
  440.          continue;
  441.       }
  442.  
  443.       /* Check to see if the pixel lies on a bank boundary. */
  444.       for(i = 0; bank_trigger_24[i] != -1; i++)
  445.       {  if (bank_trigger_24[i] == current_pixel)
  446.          {  block_code |= FL_BANK_TRIGGER_24;
  447.             bank_trigger_24[i] = 0;
  448.             break;
  449.          }
  450.       }
  451.       for(i = 0; bank_trigger_32[i] != -1; i++)
  452.       {  if (bank_trigger_32[i] == current_pixel)
  453.          {  block_code |= FL_BANK_TRIGGER_32;
  454.             bank_trigger_32[i] = 0;
  455.             break;
  456.          }
  457.       }
  458.  
  459.       /* Generate bank switch trigger block. */
  460.       if (block_code)
  461.       {  /* Check for pixel split across banks (only in 24bpp mode). */
  462.          if (current_pixel & 16383)
  463.          {  block_code |= FL_SPLIT_PIXEL;
  464.             fwrite(&block_code, 2, 1, output_file);
  465.             b = image_data[current_pixel * 3 + 0];
  466.             g = image_data[current_pixel * 3 + 1];
  467.             r = image_data[current_pixel * 3 + 2];
  468.             fwrite(&b, 1, 1, output_file);
  469.             fwrite(&g, 1, 1, output_file);
  470.             fwrite(&r, 1, 1, output_file);
  471.             current_pixel++; file_pos += 5;
  472.          }
  473.          /* No split pixel, just a nice clean bank switch. */
  474.          else
  475.          {  fwrite(&block_code, 2, 1, output_file);
  476.             file_pos += 2;
  477.          }
  478.          continue;
  479.       }
  480.  
  481.       /* Check for repeated data. */
  482.       for (repeat_count = 1, i = current_pixel * 3; repeat_count < 8191; repeat_count++, i += 3)
  483.          {  /* Break loop if end of run found. */
  484.             if ((image_data[i + 0] != image_data[i + 3]) ||
  485.                (image_data[i + 1] != image_data[i + 4]) ||
  486.                (image_data[i + 2] != image_data[i + 5])) break;
  487.             /* Don't let repeat runs span across bank boundaries. */
  488.             for(h = 0;
  489.                (bank_trigger_24[h] != -1) &&
  490.                (bank_trigger_24[h] != current_pixel + repeat_count)
  491.                ; h++);
  492.             if (bank_trigger_24[h] == current_pixel + repeat_count) break;
  493.             for(h = 0;
  494.                (bank_trigger_32[h] != -1) &&
  495.                (bank_trigger_32[h] != current_pixel + repeat_count)
  496.                ; h++);
  497.             if (bank_trigger_32[h] == current_pixel + repeat_count) break;
  498.          }
  499.  
  500.       /* Generate repeat block if repeated data was found. */
  501.       if (repeat_count > 1)
  502.       {  fwrite(&repeat_count, 2, 1, output_file);
  503.          b = image_data[current_pixel * 3 + 0];
  504.          g = image_data[current_pixel * 3 + 1];
  505.          r = image_data[current_pixel * 3 + 2];
  506.          fwrite(&b, 1, 1, output_file);
  507.          fwrite(&g, 1, 1, output_file);
  508.          fwrite(&r, 1, 1, output_file);
  509.          current_pixel += repeat_count; file_pos += 5;
  510.          continue;
  511.       }
  512.  
  513.       /* No repeated data, so put together an absolute block. */
  514.       for (absolute_count = 1, i = current_pixel * 3; absolute_count < 8191; absolute_count++, i += 3)
  515.       {  /* Don't let absolute block span across 64kb file-block boundary. */
  516.          if ((file_block_pos + 2 + absolute_count * 3) > 65531) break;
  517.          /* Don't let absolute block span across bank boundaries. */
  518.          for(h = 0;
  519.             (bank_trigger_24[h] != -1) &&
  520.             (bank_trigger_24[h] != current_pixel + absolute_count)
  521.             ; h++);
  522.          if (bank_trigger_24[h] == current_pixel + absolute_count) break;
  523.          for(h = 0;
  524.             (bank_trigger_32[h] != -1) &&
  525.             (bank_trigger_32[h] != current_pixel + absolute_count)
  526.             ; h++);
  527.          if (bank_trigger_32[h] == current_pixel + absolute_count) break;
  528.          /* End absolute block if repeated data is found. */
  529.          if ((image_data[i + 0] != image_data[i + 3]) ||
  530.             (image_data[i + 1] != image_data[i + 4]) ||
  531.             (image_data[i + 2] != image_data[i + 5])) continue;
  532.          if ((image_data[i + 3] != image_data[i + 6]) ||
  533.             (image_data[i + 4] != image_data[i + 7]) ||
  534.             (image_data[i + 5] != image_data[i + 8])) continue;
  535.          break;
  536.       }
  537.  
  538.       /* Output the absolute block to the file. */
  539.       block_code = absolute_count | FL_ABSOLUTE_BLOCK;
  540.       fwrite(&block_code, 2, 1, output_file);
  541.       for (h = 0, i = current_pixel * 3; h < absolute_count; h++, i += 3)
  542.       {  b = image_data[i + 0];
  543.          g = image_data[i + 1];
  544.          r = image_data[i + 2];
  545.          fwrite(&b, 1, 1, output_file);
  546.          fwrite(&g, 1, 1, output_file);
  547.          fwrite(&r, 1, 1, output_file);
  548.       }
  549.       current_pixel += absolute_count;
  550.       file_pos += (absolute_count * 3) + 2;
  551.    }
  552.  
  553.    /* Write end of file-block code. */
  554.    block_code = 0;
  555.    fwrite(&block_code, 2, 1, output_file);
  556.  
  557.    /* Done encoding the file. */
  558.    fclose(output_file);
  559.    return 0;
  560. }
  561.  
  562.  
  563.  
  564.  
  565. /* Included because strnicmp is not ANSI. */
  566. int simple_strnicmp(const char source[], const char dest[], int max)
  567. {  int h;
  568.  
  569.    for (h = 0; h < max; h++)
  570.    {  if ((tolower(source[h]) != tolower(dest[h])) ||
  571.          ((!source[h] || !dest[h]) && (h != (max - 1))))
  572.          return 1;
  573.    }
  574.    return 0;
  575. }